/*******************************************************************************
 * Copyright (c) 2000, 2005 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials 
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package com.architexa.org.eclipse.draw2d.text;

import com.architexa.org.eclipse.draw2d.Figure;
import com.architexa.org.eclipse.draw2d.PositionConstants;
import com.architexa.org.eclipse.draw2d.geometry.Insets;

import java.util.List;

import org.eclipse.swt.SWT;


/**
 * The layout for {@link BlockFlow} figures.
 * 
 * <P>WARNING: This class is not intended to be subclassed by clients.
 * @author hudsonr
 * @since 2.1 */
public class BlockFlowLayout
	extends FlowContainerLayout
{

BlockBox blockBox;
boolean blockInvalid = false;
private boolean continueOnSameLine = false;
private CompositeBox previousLine = null;

/**
 * Creates a new BlockFlowLayout with the given BlockFlow.
 * @param blockFlow the BlockFlow
 */
public BlockFlowLayout(BlockFlow blockFlow) {
	super(blockFlow);
}

private void addBelowPreviousLine(CompositeBox line) {
	if (previousLine == null)
		line.setLineTop(line.getTopMargin());
	else
		line.setLineTop(previousLine.getBaseline() + previousLine.getDescent()
				+ Math.max(previousLine.getBottomMargin(), line.getTopMargin()));

	int alignment = getBlockFlow().getHorizontalAligment();
	if (alignment == PositionConstants.LEFT || alignment == PositionConstants.RIGHT) {
		int orientation = getBlockFlow().getOrientation();
		if (alignment == PositionConstants.LEFT)
			alignment = orientation == SWT.LEFT_TO_RIGHT
					? PositionConstants.ALWAYS_LEFT : PositionConstants.ALWAYS_RIGHT;
		else
			alignment = orientation == SWT.LEFT_TO_RIGHT
					? PositionConstants.ALWAYS_RIGHT : PositionConstants.ALWAYS_LEFT;
	}
	if (alignment != PositionConstants.CENTER && getBlockFlow().isMirrored())
		alignment = (PositionConstants.ALWAYS_LEFT | PositionConstants.ALWAYS_RIGHT) 
				& ~alignment;

	switch (alignment) {
		case PositionConstants.ALWAYS_RIGHT:
			line.setX(blockBox.getRecommendedWidth() - line.getWidth());
			break;
		case PositionConstants.CENTER:
			line.setX((blockBox.getRecommendedWidth() - line.getWidth()) / 2);
			break;
		case PositionConstants.ALWAYS_LEFT:
			line.setX(0);
			break;
		default:
			throw new RuntimeException("Unexpected state"); //$NON-NLS-1$
	}
	blockBox.add(line);
	previousLine = line;
}

/**
 * Align the line horizontally and then commit it.
 */
protected void addCurrentLine() {
	addBelowPreviousLine(currentLine);	
	((LineRoot)currentLine).commit();
}

/**
 * @see FlowContext#addLine(CompositeBox)
 */
public void addLine(CompositeBox box) {
	endLine();
	addBelowPreviousLine(box);
}

/**
 * Marks the blocks contents as changed.  This means that children will be invalidated
 * during validation.
 * @since 3.1
 */
public void blockContentsChanged() {
	blockInvalid = true;
}

/**
 * @see FlowContainerLayout#cleanup()
 */
protected void cleanup() {
	super.cleanup();
	previousLine = null;
}

/**
 * @see FlowContainerLayout#createNewLine()
 */
protected void createNewLine() {
	currentLine = new LineRoot(getBlockFlow().isMirrored());
	currentLine.setRecommendedWidth(blockBox.getRecommendedWidth());
}

/**
 * Called by flush(), adds the BlockBox associated with this BlockFlowLayout
 * to the current line and then ends the line.
 */
protected void endBlock() {
	if (blockInvalid) {
		Insets insets = getBlockFlow().getInsets();
		blockBox.height += insets.getHeight();
		blockBox.width += insets.getWidth();
	}

	if (getContext() != null)
		getContext().addLine(blockBox);

	if (blockInvalid) {
		blockInvalid = false;
		List v = getFlowFigure().getChildren();
		for (int i = 0; i < v.size(); i++)
			((FlowFigure)v.get(i)).postValidate();
	}
}

/**
 * @see FlowContext#endLine()
 */
public void endLine() {
	if (currentLine == null || !currentLine.isOccupied())
		return;
	addCurrentLine();
	currentLine = null;
}

/**
 * @see FlowContainerLayout#flush()
 */
protected void flush() {
	endLine();
	endBlock();
}

boolean forceChildInvalidation(Figure f) {
	return blockInvalid;
}

/**
 * Returns the BlockFlow associated with this BlockFlowLayout
 * @return the BlockFlow
 */
protected final BlockFlow getBlockFlow() {
	return (BlockFlow)getFlowFigure();
}

int getContextWidth() {
	return getContext().getRemainingLineWidth();
}

/**
 * @see FlowContext#getContinueOnSameLine()
 */
public boolean getContinueOnSameLine() {
	return continueOnSameLine;
}

/**
 * @see FlowContext#getWidthLookahead(FlowFigure, int[])
 */
public void getWidthLookahead(FlowFigure child, int result[]) {
	List children = getFlowFigure().getChildren();
	int index = -1;
	if (child != null)
		index = children.indexOf(child);
	
	for (int i = index + 1; i < children.size(); i++)
		if (((FlowFigure)children.get(i)).addLeadingWordRequirements(result))
			return;
}

/**
 * @see FlowContainerLayout#preLayout()
 */
protected void preLayout() {
	setContinueOnSameLine(false);
	blockBox = getBlockFlow().getBlockBox();
	setupBlock();
	//Probably could setup current and previous line here, or just previous
}

/**
 * @see com.architexa.org.eclipse.draw2d.text.FlowContext#setContinueOnSameLine(boolean)
 */
public void setContinueOnSameLine(boolean value) {
	continueOnSameLine = value;
}

/**
 * sets up the single block that contains all of the lines.
 */
protected void setupBlock() {
	int recommended = getContextWidth();
	if (recommended == Integer.MAX_VALUE)
		recommended = -1;
	BlockFlow bf = getBlockFlow();
	if (recommended > 0) {
		int borderCorrection = bf.getInsets().getWidth() + bf.getLeftMargin() 
				+ bf.getRightMargin();
		recommended = Math.max(0, recommended - borderCorrection);
	}
	
	if (recommended != blockBox.recommendedWidth) {
		blockInvalid = true;
		blockBox.setRecommendedWidth(recommended);
	}
	
	if (blockInvalid) {
		blockBox.height = 0;
		blockBox.setWidth(Math.max(0, recommended));
	}
}

}